package com.pocket.demo.core.mq.rabbitMQ.demo;

import cn.hutool.json.JSONUtil;
import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitMessagingTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.Scheduled;

import java.util.HashMap;
import java.util.Map;

/**
 * <p> 死信交换机 + TTL 实现延迟队列案例【demo-MQ-rabbitMQ-7】<br>
 * <p> 实现步骤: <br>
 *      1. 创建 死信交换机 和 死信队列，并将 死信队列 绑定到 死信交换机 上 <br>
 *      2. 创建 延时交换机 和 过期队列（给过期队列设置 死信交换机 和 路由key），并将 过期队列 绑定到 延时交换机 上 <br>
 *      3. 发送消息到延迟队列 <br>
 *      4. 过期时间后，消息自动转发到死信队交换机 <br>
 *      tips: 如果原消息有 RoutingKey, 则转发到死信交换机时会沿用之前的 RoutingKey，如果死信队列没指定相同 RoutingKey 则会丢失。 <br>  <br>
 * <p> 参考地址:
 *      1. https://blog.csdn.net/qq_33204709/article/details/135445630 <br>
 * @author cookie.
 **/
//@Configuration
public class TTLDeadExchangeConfig {

    private static final Logger LOGGER = LoggerFactory.getLogger(TTLDeadExchangeConfig.class);
    static final String TTL_EXCHANGE = "pocket.ttl.exchange";
    static final String TTL_QUEUE = "pocket.ttl.queue";
    static final String TTL_ROUTING_KEY = "ttl";
    static final String TTL_DEAD_EXCHANGE = "pocket.ttl.dead.exchange";
    static final String TTL_DEAD_QUEUE = "pocket.ttl.dead.queue";


    // 步骤一：创建死信交换机
    @Bean
    public DirectExchange deadExchange(){
        return new DirectExchange(TTL_DEAD_EXCHANGE, true, false);
    }

    // 步骤二：创建死信队列
    @Bean
    public Queue deadQueue() {
        return new Queue(TTL_DEAD_QUEUE, true, true, false);
    }

    // 步骤三：绑定 死信队列 到 死信交换机
    @Bean
    public Binding bindingDeadQueue(Queue deadQueue, DirectExchange deadExchange) {
        return BindingBuilder.bind(deadQueue).to(deadExchange).with(TTL_ROUTING_KEY);
    }


    // 步骤四：定义延时交换机
    @Bean
    public DirectExchange ttlExchange() {
        return new DirectExchange(TTL_EXCHANGE, true, false, null);
    }

    // 步骤五：定义过期消息队列，设置消息过期时间，指定死信队列
    @Bean
    public Queue ttlQueue() {
        // x-message-ttl 消息过期时间，单位毫秒
        // x-dead-letter-exchange 死信交换机
        // x-dead-letter-routing-key 死信路由键。消息变为死信并投递到死信交换机时，会沿用之前的RoutingKey
        Map<String, Object> arguments = new HashMap<>();
        arguments.put("x-message-ttl", 10000);
        arguments.put("x-dead-letter-exchange", TTL_DEAD_EXCHANGE);
        arguments.put("x-dead-letter-routing-key", TTL_ROUTING_KEY);
        return new Queue(TTL_QUEUE, true, true, false, arguments);
    }

    // 步骤六：绑定 过期消息队列 到 延时交换机
    @Bean
    public Binding bindingQueue(Queue ttlQueue, DirectExchange ttlExchange) {
        return BindingBuilder.bind(ttlQueue).to(ttlExchange).with(TTL_ROUTING_KEY);
    }


    @Resource private RabbitMessagingTemplate rabbitMessagingTemplate;

    // 步骤七：发送消息到延迟队列
    @Scheduled(fixedRate = 100000, initialDelay = 1000)
    public void sendMessage() {
        rabbitMessagingTemplate.convertAndSend(TTL_EXCHANGE, TTL_ROUTING_KEY, "Scheduled message test");
    }

    // 步骤八：接收死信消息 根据当前配置 10秒后将将收到 Scheduled message test。
    @RabbitListener(queues = TTL_DEAD_QUEUE)
    public void listenDirectWarnQueueMessage(Message message, MessageProperties messageProperties){
        LOGGER.debug("RabbitMQ （all params）: listener received param [amqpMessage]：{}", message);
        LOGGER.debug("RabbitMQ （all params）: listener received param [messageProperties]：{}", JSONUtil.toJsonStr(messageProperties));
    }

}
